/* * #%L * pro-grade * %% * Copyright (C) 2013 - 2014 Ondřej Lukáš, Josef Cacek * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * #L% */ package net.sourceforge.prograde.generator; import java.io.File; import java.io.FilePermission; import java.io.IOException; import java.io.PrintWriter; import java.security.AccessController; import java.security.CodeSource; import java.security.Permission; import java.security.PrivilegedAction; import java.security.ProtectionDomain; import java.util.Collections; import java.util.Date; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; /** * DeniedPermissionListener implementation which generates a policy file with the denied permissions. * <p> * File to which is the policy written is either provided to a constructor or value of system property * {@value #PROGRADE_GENERATED_POLICY} is used as the file path. When neither a file instance nor the system property is * provided a temporary file is created using {@link File#createTempFile(String, String)}. * * @author Josef Cacek */ public final class GeneratePolicyFromDeniedPermissions implements DeniedPermissionListener { /** * System property name for setting generated policy file path when the file is not specified in the constructor. */ public static final String PROGRADE_GENERATED_POLICY = "prograde.generated.policy"; private final PrivilegedAction<Void> WRITE_TO_FILE_ACTION = new PrivilegedAction<Void>() { @Override public Void run() { writeToFile(); return null; } }; private final Map<CodeSource, Set<Permission>> missingPermissions = Collections .synchronizedMap(new TreeMap<CodeSource, Set<Permission>>(new CodesourceComparator())); private final File file; private boolean refreshed = false; private final FilePermission filePermissionToSkip; /** * Default constructor. */ public GeneratePolicyFromDeniedPermissions() { this(null); } /** * Constructor. * * @param targetFile file to which the policy generated from denied permissions will be written */ public GeneratePolicyFromDeniedPermissions(final File targetFile) { if (targetFile != null) { file = targetFile; } else { String sysProp = SecurityActions.getSystemProperty(PROGRADE_GENERATED_POLICY); if (sysProp != null) { file = new File(sysProp); } else { try { file = File.createTempFile("generated-", ".policy"); System.err.println("Writing policy to temporary file: " + file.getAbsolutePath()); } catch (IOException e) { throw new RuntimeException("Unable to create a new policy file", e); } } } filePermissionToSkip = new FilePermission(file.getPath(), "write"); } /** * Writes the given permission under the grant entry with codesource from given {@link ProtectionDomain} into the generated * policy file. * * @see net.sourceforge.prograde.generator.DeniedPermissionListener#permissionDenied(java.security.ProtectionDomain, * java.security.Permission) */ @Override public void permissionDenied(final ProtectionDomain pd, final Permission perm) { if (filePermissionToSkip.equals(perm)) { return; } final CodeSource codeSource = pd.getCodeSource(); Set<Permission> permSet = missingPermissions.get(codeSource); if (permSet == null) { synchronized (missingPermissions) { permSet = missingPermissions.get(codeSource); if (permSet == null) { permSet = Collections.synchronizedSet(new TreeSet<Permission>(new PermissionComparator())); missingPermissions.put(codeSource, permSet); } } } if (permSet.add(perm)) { AccessController.doPrivileged(WRITE_TO_FILE_ACTION); } } /** * Clears generated policy file. * * @see net.sourceforge.prograde.generator.DeniedPermissionListener#policyRefreshed() */ @Override public void policyRefreshed() { synchronized (missingPermissions) { refreshed = true; } AccessController.doPrivileged(WRITE_TO_FILE_ACTION); } private void writeToFile() { PrintWriter pw = null; final String className = getClass().getSimpleName(); synchronized (missingPermissions) { try { pw = new PrintWriter(file, "UTF-8"); pw.println("// " + className + " - timestamp: " + new Date().toString()); if (refreshed) { pw.println("// The policy was refreshed already."); } pw.println(); for (Map.Entry<CodeSource, Set<Permission>> csEntry : missingPermissions.entrySet()) { pw.println("grant codeBase \"" + csEntry.getKey().getLocation() + "\" {"); for (Permission p : csEntry.getValue()) { pw.print(" permission " + p.getClass().getName()); if (p.getName() != null) { pw.print(" \"" + p.getName() + "\""); } if (p.getActions() != null && !p.getActions().equals("")) { pw.print(", \"" + p.getActions() + "\""); } pw.println(";"); } pw.println("};"); pw.println(); } pw.println("// " + className + " - That's all"); } catch (Exception e) { e.printStackTrace(); } finally { if (pw != null) { try { pw.close(); } catch (Exception e) { e.printStackTrace(); } } } } } }